package recallin.database;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.zip.ZipException;

import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.database.Cursor;
import android.location.Location;
import android.os.Environment;
import android.os.Handler;
import android.util.Log;

import com.db4o.Db4oEmbedded;
import com.db4o.ObjectContainer;
import com.db4o.query.Predicate;

/**
 * Takes care of all outgoing connections to a online database by using json.
 * 
 * @author Ingimar Samuelsson
 * 
 */
public class ServerConnection {

	@SuppressWarnings("unused")
	private Thread thread;
	private Friends user = null;
	private static final String server = "http://www.recallin.com/_action/_phone.jsp?drowssap=X43KKjjdsl3w24oo&action=";
	private JSONObject jObject;
	public final static String DB4OFILENAME = Environment
			.getExternalStorageDirectory().getPath()
			+ "/Recallin/Database/"
			+ "/user_info.db4o";

	private ObjectContainer db;
	private boolean online;
	private Cursor cursor;

	public ServerConnection() {
		try {
			Log.i("LocalDatabase", "Opening database");
			db = Db4oEmbedded.openFile(DB4OFILENAME);
		} catch (Exception e) {
			Log.i("LocalDatabase", "Creating new database");
			try {
				new File(Environment.getExternalStorageDirectory().getPath()
						+ "/Recallin/Database/").mkdir();
				new File(DB4OFILENAME).createNewFile();
				db = Db4oEmbedded.openFile(Db4oEmbedded.newConfiguration(),
						DB4OFILENAME);
			} catch (IOException e1) {
				e1.printStackTrace();
			}
		} finally {
			db.close();
		}
	}

	public Friends getUser() {
		return user;
	}

	public void turnOnLocation() {
		thread = new Thread() {
			public void run() {
				while (true != false) {

					// / USER POSITION IS MISSING !

					updateLocation(Double.toString(Math.random()),
							Double.toString(Math.random()));
					try {
						Thread.sleep(3600000);
					} catch (InterruptedException e) {
					}
				}
			}
		};
	}

	public void turnOffLocation() {
		this.thread = null;
	}

	public void addFriend(int friendID) {
		String URL = server + "add_friend&userID=" + user.getUser_id()
				+ "&friendID=" + friendID;
		postInfo(URL);
	}

	public void deleteFriend(int friendID) {
		String URL = server + "delete_friend&userID=" + user.getUser_id()
				+ "&friendID=" + friendID;
		postInfo(URL);
	}

	public void rejectFriend(int friendID) {
		String URL = server + "reject_friend&userID=" + user.getUser_id()
				+ "&friendID=" + friendID;
		postInfo(URL);
	}

	public void acceptFriend(int friendID) {
		String URL = server + "accept_friend&userID=" + user.getUser_id()
				+ "&friendID=" + friendID;
		postInfo(URL);
	}

	public String login(String phone_number, String password) {
		String check = "";
		try {
			check = getJSON(server + "login&phone_number=" + phone_number
					+ "&password=" + password);
			setOnline(true);
		} catch (NullPointerException e) {
			setOnline(false);
		}
		if (check.trim().equals("User not found.")) {
			return check.trim();
		} else if (isOnline()) {
			try {
				jObject = new JSONObject(check);
				JSONArray obj = jObject.getJSONArray("users");
				for (int i = 0; i < obj.length(); i++) {
					user = new Friends(obj.getJSONObject(i).getInt("user_id"),
							obj.getJSONObject(i).getString("name"),
							obj.getJSONObject(i).getString("phone_number"), obj
									.getJSONObject(i).getString("email"), obj
									.getJSONObject(i).getInt("hidden_all"));
				}

				return user.getName();
			} catch (JSONException e) {
				check = e.getMessage();
			}
		}
		return check;
	}

	private void updateLocation(String longitude, String latitude) {
		String URL = server + "set&phone_number=" + this.user.getPhone_number()
				+ "&longitude=" + longitude + "&latitude=" + latitude;
		postInfo(URL);
	}

	public void syncFriends() throws NullPointerException {
		db = Db4oEmbedded.openFile(DB4OFILENAME);
		try {
			jObject = new JSONObject(getJSON(server + "search_friends&userID="
					+ user.getUser_id() + "&search=" + ""));
			JSONArray friends = jObject.getJSONArray("friends");

			for (int i = 0; i < friends.length(); i++) {
				System.out.println("----");
				final Friends onlineUser = new Friends(friends.getJSONObject(i)
						.getInt("user_id"), friends.getJSONObject(i).getString(
						"name"), friends.getJSONObject(i).getString(
						"phone_number"), friends.getJSONObject(i).getString(
						"email"), friends.getJSONObject(i).getDouble("lon"),
						friends.getJSONObject(i).getDouble("lat"), friends
								.getJSONObject(i).getString("last_time"),
						friends.getJSONObject(i).getInt("location_accuracy"),
						friends.getJSONObject(i).getInt("hidden_all"), friends
								.getJSONObject(i).getInt("request_status"),
						friends.getJSONObject(i).getInt("hidden_friend"),
						friends.getJSONObject(i).getInt("me_hidden"),
						lastSeen(friends.getJSONObject(i)
								.getString("last_time")), friends
								.getJSONObject(i).getInt("me_request"));

				onlineUser.setTempStatus(checkFriendStatus(onlineUser));

				String url = getGoogleURL(onlineUser.getLatitude(),
						onlineUser.getLongitude());
				JSONObject jObject = new JSONObject(getJSON(url));
				JSONArray array = jObject.getJSONArray("results");
				if (array.length() > 0) {
					String[] tmp = array.getJSONObject(0)
							.getString("formatted_address").split(", ");
					onlineUser.setTempAddress(tmp[0] + ",\n" + tmp[1]);
				}

				List<Friends> inBoth = db.query(new Predicate<Friends>() {
					@Override
					public boolean match(Friends friend) {
						if (friend.getUser_id() == onlineUser.getUser_id()) {
							return true;
						}
						return false;
					}
				});

				for (Friends friends2 : inBoth) {
					System.out.println(friends2.getName());
				}

				if (inBoth.size() != 0) {
					for (Friends friend : inBoth) {
						switch (friend.getStatus()) {
						case Friends.DELETED:
							deleteFriend(friend.getUser_id());
							System.out.println(friend.getName());
							db.delete(friend);
							break;
						case Friends.NEW:
							addFriend(friend.getUser_id());
							db.delete(friend);
							friend.setStatus(Friends.NUTURAL);
							db.store(friend);
							break;
						case Friends.CHANGED:
							if (friend.getMe_hidden() == 0)
								toggleFriendVisibility(friend.getUser_id(),
										true);
							else
								toggleFriendVisibility(friend.getUser_id(),
										false);
							db.delete(friend);
							friend.setStatus(Friends.NUTURAL);
							db.store(friend);
							break;
						case Friends.NUTURAL:
							db.delete(friend);
							db.store(onlineUser);
							break;
						default:
							break;
						}
					}
				} else
					db.store(onlineUser);

			}
		} catch (JSONException e) {
			e.printStackTrace();
		} finally {
			db.close();
		}

	}

	public ArrayList<Friends> searchUsers(String search)
			throws NullPointerException {

		ArrayList<Friends> people = new ArrayList<Friends>();
		try {
			jObject = new JSONObject(getJSON(this.server
					+ "search_users&userID=" + user.getUser_id() + "&search="
					+ search));
			JSONArray users = jObject.getJSONArray("people");
			for (int i = 0; i < users.length(); i++) {
				people.add(new Friends(
						users.getJSONObject(i).getInt("user_id"), users
								.getJSONObject(i).getString("name")));
			}
		} catch (JSONException e) {
			e.printStackTrace();
		}
		return people;

	}

	private String getGoogleURL(Double lat, Double lon) {
		return "http://maps.googleapis.com/maps/api/geocode/json?latlng=" + lat
				+ "," + lon + "&sensor=true";
	}

	private void addFavorite(Place newFavorite) {

		String URL = server + "add_favorite&userID=" + this.user.getUser_id()
				+ "&favorite_name=" + newFavorite.getFavorite_name() + "&name="
				+ newFavorite.getName() + "&street=" + newFavorite.getStreet()
				+ "&city=" + newFavorite.getCity() + "&state="
				+ newFavorite.getState() + "&country="
				+ newFavorite.getCountry() + "&lon="
				+ newFavorite.getLongitude() + "&lat="
				+ newFavorite.getLatitude() + "&type=" + newFavorite.getType();

		postInfo(URL);
	}

	private void deleteFavorite(Place place) {
		String URL = server + "remove_favorite&favorite_id="
				+ place.getFavorite_id();
		postInfo(URL);
	}

	public boolean syncFavorites() throws NullPointerException {
		Log.i("Favorites", "Updating favorites");
		// syncOnlineFav();
		try {
			jObject = new JSONObject(getJSON(this.server
					+ "search_favorites&userID=" + user.getUser_id()));
			JSONArray fav = jObject.getJSONArray("favorites");
			for (int i = 0; i < fav.length(); i++) {
				// String name,
				// String street,
				// String city,
				// String state,
				// String country,
				// double latitude,
				// double longitude,
				// String type,
				// String favorite_name
				final Place onlineFavPlace = new Place(fav.getJSONObject(i)
						.getInt("fav_id"), fav.getJSONObject(i).getString(
						"name"), fav.getJSONObject(i).getString("street"), fav
						.getJSONObject(i).getString("city"), fav.getJSONObject(
						i).getString("country"), fav.getJSONObject(i)
						.getDouble("lat"), fav.getJSONObject(i)
						.getDouble("lon"), fav.getJSONObject(i).getString(
						"type"), fav.getJSONObject(i)
						.getString("favorite_name"), fav.getJSONObject(i)
						.getInt("isDeleted"));

				cursor = DatabaseUtility.getDatabase().rawQuery(
						"SELECT * FROM places WHERE latitude="
								+ onlineFavPlace.getLatitude()
								+ " AND longitude="
								+ onlineFavPlace.getLongitude()
								+ " AND favorite_name IS null"
								+ " AND type!='street'", null);
				while (cursor.moveToNext()) {
					Log.d("SERVERCONNECTION", "Is in table but not favorite: "
							+ cursor.getString(11));
					// DatabaseUtility.getDatabase().execSQL(
					// "UPDATE places SET favorite_name='"
					// + onlineFavPlace.getFavorite_name()
					// + "', status=" + Place.NUTURAL);
				}

				cursor = DatabaseUtility.getDatabase().rawQuery(
						"SELECT * FROM places WHERE latitude="
								+ onlineFavPlace.getLatitude()
								+ " AND longitude="
								+ onlineFavPlace.getLongitude()
								+ " AND type='street'", null);
				if (cursor.getCount() == 0) {
					Log.d("SERVERCONNECTION", "Does not exist adding : "
							+ cursor.getString(11));
					// DatabaseUtility
					// .getDatabase()
					// .execSQL(
					// "INSERT INTO places (category,name,street,city,state,country,latitude,longitude,type,favorite_name,status) VALUES "
					// + "('"
					// + onlineFavPlace.getCategory()
					// + "', '"
					// + onlineFavPlace.getName()
					// + "', '"
					// + onlineFavPlace.getStreet()
					// + "', '"
					// + onlineFavPlace.getCity()
					// + "', '"
					// + onlineFavPlace.getState()
					// + "', '"
					// + onlineFavPlace.getCountry()
					// + "', "
					// + onlineFavPlace.getLatitude()
					// + ", "
					// + onlineFavPlace.getLongitude()
					// + ", '"
					// + onlineFavPlace.getType()
					// + "', '"
					// + onlineFavPlace.getFavorite_name()
					// + "', '" + Place.NUTURAL + "')");
				}

			}

		} catch (JSONException e) {
			e.printStackTrace();
			return false;
		}
		return true;
	}

	private void syncOnlineFav() {
		cursor = DatabaseUtility.getDatabase().rawQuery(
				"SELECT * FROM places WHERE status=" + Place.NEW + "", null);

		while (cursor.moveToNext()) {
			Place newFavorite = new Place(cursor.getString(3),
					cursor.getString(4), cursor.getString(5),
					cursor.getString(6), cursor.getString(7),
					cursor.getDouble(9), cursor.getDouble(8),
					cursor.getString(10), cursor.getString(11),
					cursor.getString(0));
			addFavorite(newFavorite);
			DatabaseUtility.getDatabase().execSQL(
					"UPDATE places SET status=" + Place.NUTURAL
							+ " WHERE latitude=" + cursor.getDouble(9)
							+ " AND longitude=" + cursor.getDouble(8));
		}

		cursor = DatabaseUtility.getDatabase()
				.rawQuery(
						"SELECT * FROM places WHERE status=" + Place.DELETED
								+ "", null);
		while (cursor.moveToNext()) {
			Place deleted = new Place(cursor.getString(3), cursor.getString(4),
					cursor.getString(5), cursor.getString(6),
					cursor.getString(7), cursor.getDouble(9),
					cursor.getDouble(8), cursor.getString(10),
					cursor.getString(11), cursor.getString(0));
			deleteFavorite(deleted);
			DatabaseUtility.getDatabase().execSQL(
					"DELETE FROM places WHERE type='street'");
			DatabaseUtility.getDatabase().execSQL(
					"UPDATE places SET status=" + Place.NUTURAL
							+ " WHERE latitude=" + cursor.getDouble(9)
							+ " AND longitude=" + cursor.getDouble(8));
		}
	}

	private static boolean postInfo(String URL) {
		HttpClient client = new DefaultHttpClient();
		try {
			client.execute(new HttpGet(URL));
			return true;
		} catch (ClientProtocolException e) {
			return false;
		} catch (IOException e) {
			return false;
		}
	}

	// private String receiveInfo(String URL) {
	// HttpClient client = new DefaultHttpClient();
	// StringBuilder sb = new StringBuilder();
	// try {
	// HttpResponse response = client.execute(new HttpGet(URL));
	//
	// InputStream is = response.getEntity().getContent();
	// BufferedReader reader = new BufferedReader(
	// new InputStreamReader(is));
	//
	// String line = null;
	// try {
	// while ((line = reader.readLine()) != null) {
	// sb.append(line + "\n");
	// }
	// } catch (IOException e) {
	// return "read: " + e.getMessage();
	// } finally {
	// try {
	// is.close();
	// } catch (IOException e) {
	// return "close: " + e.getMessage();
	// }
	// }
	// } catch (ClientProtocolException e) {
	// return "client: " + e.getMessage();
	// } catch (IOException e) {
	// return "whole: " + e.getMessage();
	// }
	// return sb.toString();
	// }

	public String lastSeen(String input) {
		try {
			input = input.trim();
			input = input.replace('-', ' ');
			input = input.replace('.', ' ');
			input = input.replace(':', ' ');
			String[] split = input.split(" ");
			Calendar l = new GregorianCalendar();
			l.set(Integer.parseInt(split[0]), Integer.parseInt(split[1]) - 1,
					Integer.parseInt(split[2]), Integer.parseInt(split[3]),
					Integer.parseInt(split[4]));

			Date last = l.getTime();
			Date current = Calendar.getInstance().getTime();

			int mins = (int) ((current.getTime() - last.getTime()) / (1000 * 60));
			if (mins == 0) {
				return "Just now";
			} else if (mins == 1) {
				return mins + " minute ago:";
			} else if (mins > 0 && mins < 60) {
				return mins + " minutes ago:";
			} else if (mins > 59 && mins < 120) {
				return mins / 60 + " hour ago:";
			} else if (mins > 59 && mins < 2879) {
				return mins / 60 + " hours ago:";
			} else if (mins > 2879 && mins < 20159) {
				return mins / (60 * 24) + " days ago:";
			} else if (mins > 20159)
				return mins / (60 * 24 * 7) + " weeks ago:";

			return "unknown";

		} catch (Exception e) {
			return null;
		}
	}

	private static String getJSON(String url) {
		String json = "";
		try {
			URL google = new URL(url);
			URLConnection yc = google.openConnection();
			BufferedReader in = new BufferedReader(new InputStreamReader(
					yc.getInputStream()));
			String inputLine;
			while ((inputLine = in.readLine()) != null) {
				json += inputLine;

			}
			in.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return json;
	}

	public void toggleVisibility(boolean visible) {
		String s = "show_location_all";
		if (!visible)
			s = "hide_location_all";

		String URL = server + s + "&userID=" + this.getUser().getUser_id();
		postInfo(URL);
	}

	public void toggleFriendVisibility(int friendID, boolean visible) {
		String s = "show_location_friend";
		if (!visible)
			s = "hide_location_friend";

		String URL = server + s + "&userID=" + this.getUser().getUser_id()
				+ "&friendID=" + friendID;
		postInfo(URL);
	}

	private String checkFriendStatus(Friends friends) {
		if (friends.getRequest_status() == 0 && friends.getMe_request() == 1) {
			return "Pending";
		} else if (friends.getRequest_status() == 1
				&& friends.getMe_request() == 1) {
			return "Friends";
		} else
			return "Request";
	}

	public void closeDatabase() {
		db.close();
	}

	public static boolean isOnline() {
		return postInfo(server);
	}

	public void setOnline(boolean online) {
		this.online = online;
	}

}
